home *** CD-ROM | disk | FTP | other *** search
/ Total Network Tools 2002 / NextStepPublishing-TotalNetworkTools2002-Win95.iso / Archive / Misc Servers / Zope.exe / UNINDEX.PY < prev    next >
Encoding:
Python Source  |  2000-11-13  |  11.0 KB  |  339 lines

  1. ##############################################################################
  2. # Zope Public License (ZPL) Version 1.0
  3. # -------------------------------------
  4. # Copyright (c) Digital Creations.  All rights reserved.
  5. # This license has been certified as Open Source(tm).
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions are
  8. # met:
  9. # 1. Redistributions in source code must retain the above copyright
  10. #    notice, this list of conditions, and the following disclaimer.
  11. # 2. Redistributions in binary form must reproduce the above copyright
  12. #    notice, this list of conditions, and the following disclaimer in
  13. #    the documentation and/or other materials provided with the
  14. #    distribution.
  15. # 3. Digital Creations requests that attribution be given to Zope
  16. #    in any manner possible. Zope includes a "Powered by Zope"
  17. #    button that is installed by default. While it is not a license
  18. #    violation to remove this button, it is requested that the
  19. #    attribution remain. A significant investment has been put
  20. #    into Zope, and this effort will continue if the Zope community
  21. #    continues to grow. This is one way to assure that growth.
  22. # 4. All advertising materials and documentation mentioning
  23. #    features derived from or use of this software must display
  24. #    the following acknowledgement:
  25. #      "This product includes software developed by Digital Creations
  26. #      for use in the Z Object Publishing Environment
  27. #      (http://www.zope.org/)."
  28. #    In the event that the product being advertised includes an
  29. #    intact Zope distribution (with copyright and license included)
  30. #    then this clause is waived.
  31. # 5. Names associated with Zope or Digital Creations must not be used to
  32. #    endorse or promote products derived from this software without
  33. #    prior written permission from Digital Creations.
  34. # 6. Modified redistributions of any form whatsoever must retain
  35. #    the following acknowledgment:
  36. #      "This product includes software developed by Digital Creations
  37. #      for use in the Z Object Publishing Environment
  38. #      (http://www.zope.org/)."
  39. #    Intact (re-)distributions of any official Zope release do not
  40. #    require an external acknowledgement.
  41. # 7. Modifications are encouraged but must be packaged separately as
  42. #    patches to official Zope releases.  Distributions that do not
  43. #    clearly separate the patches from the original work must be clearly
  44. #    labeled as unofficial distributions.  Modifications which do not
  45. #    carry the name Zope may be packaged in any form, as long as they
  46. #    conform to all of the clauses above.
  47. # Disclaimer
  48. #   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
  49. #   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  50. #   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  51. #   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
  52. #   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  53. #   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  54. #   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  55. #   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  56. #   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  57. #   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  58. #   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  59. #   SUCH DAMAGE.
  60. # This software consists of contributions made by Digital Creations and
  61. # many individuals on behalf of Digital Creations.  Specific
  62. # attributions are listed in the accompanying credits file.
  63. ##############################################################################
  64.  
  65. """Simple column indices"""
  66. __version__='$Revision: 1.11.2.4 $'[11:-2]
  67.  
  68. from Globals import Persistent
  69. from Acquisition import Implicit
  70. import BTree
  71. import IOBTree
  72. from intSet import intSet
  73. import operator
  74. from Missing import MV
  75. import string, pdb
  76. from zLOG import LOG, ERROR
  77.  
  78. ListType=type([])
  79. StringType=type('s')
  80.  
  81.  
  82. def nonEmpty(s):
  83.     "returns true if a non-empty string or any other (nonstring) type"
  84.     if type(s) is StringType:
  85.         if s: return 1
  86.         else: return 0
  87.     else:
  88.         return 1
  89.  
  90.  
  91. class UnIndex(Persistent, Implicit):
  92.     """UnIndex object interface"""
  93.  
  94.     meta_type = 'Field Index'
  95.  
  96.     def __init__(self, id=None, ignore_ex=None, call_methods=None):
  97.         """Create an unindex
  98.  
  99.         UnIndexes are indexes that contain two index components, the
  100.         forward index (like plain index objects) and an inverted
  101.         index.  The inverted index is so that objects can be unindexed 
  102.         even when the old value of the object is not known.
  103.  
  104.         The arguments are:
  105.  
  106.           'id' -- the name of the item attribute to index.  This is
  107.           either an attribute name or a record key.
  108.  
  109.           'ignore_ex' -- should be set to true if you want the index
  110.           to ignore exceptions raised while indexing instead of
  111.           propagating them.
  112.  
  113.           'call_methods' -- should be set to true if you want the index 
  114.           to call the attribute 'id' (note: 'id' should be callable!)
  115.           You will also need to pass in an object in the index and
  116.           uninded methods for this to work.
  117.  
  118.         """
  119.     ######################################################################
  120.     # For b/w compatability, have to allow __init__ calls with zero args
  121.  
  122.         if not id==ignore_ex==call_methods==None:
  123.             self.id = id
  124.             self.ignore_ex=ignore_ex        # currently unimplimented
  125.             self.call_methods=call_methods
  126.             self._index = BTree.BTree()
  127.             self._unindex = IOBTree.BTree()
  128.  
  129.         else:
  130.             pass
  131.  
  132.  
  133.     def __len__(self):
  134.         return len(self._unindex)
  135.  
  136.     def index_object(self, i, obj, threshold=None):
  137.         """ index and object 'obj' with integer id 'i'"""
  138.  
  139.         # Before we do anything, unindex the object we've been handed, as
  140.         # we can't depend on the user to do the right thing.
  141.         self.unindex_object(i)
  142.         
  143.         index = self._index
  144.         unindex = self._unindex
  145.  
  146.         id = self.id
  147.  
  148.         try:
  149.             k=getattr(obj, id)
  150.             if callable(k):
  151.                 k = k()
  152.         except:
  153.             k = MV
  154.  
  155. ##        if k is None or k == MV:
  156. ##            return 0
  157.  
  158.         set = index.get(k)
  159.         if set is None:
  160.             index[k] = set = intSet()
  161.             
  162.         set.insert(i)
  163.         unindex[i] = k
  164.  
  165.  
  166.         self._index = index
  167.         self._unindex = unindex
  168.  
  169.         return 1
  170.     
  171.  
  172.     def unindex_object(self, i):
  173.         """ Unindex the object with integer id 'i' and don't
  174.         raise an exception if we fail """
  175.         index = self._index
  176.         unindex = self._unindex
  177.  
  178.         k = unindex.get(i, None)
  179.         if k is None:
  180.             return None
  181.         set = index.get(k, None)
  182.         if set is not None:
  183.             try:
  184.                 set.remove(i)
  185.             except:
  186.                 LOG('UnIndex', ERROR, ('unindex_object could not remove '
  187.                                        'integer id %s from index %s.  This '
  188.                                        'should not happen.'
  189.                                        % (str(i), str(k)))) 
  190.         else:
  191.             LOG('UnIndex', ERROR, ('unindex_object tried to retrieve set %s '
  192.                                    'from index %s but couldn\'t.  This '
  193.                                    'should not happen.' % (repr(set),str(k))))
  194.  
  195.         del unindex[i]
  196.         
  197.         self._index = index
  198.         self._unindex = unindex
  199.  
  200.  
  201.     def _apply_index(self, request, cid=''): 
  202.         """Apply the index to query parameters given in the argument,
  203.         request
  204.  
  205.         The argument should be a mapping object.
  206.  
  207.         If the request does not contain the needed parameters, then
  208.         None is returned.
  209.  
  210.         If the request contains a parameter with the name of the
  211.         column + '_usage', it is sniffed for information on how to
  212.         handle applying the index.
  213.  
  214.         Otherwise two objects are returned.  The first object is a
  215.         ResultSet containing the record numbers of the matching
  216.         records.  The second object is a tuple containing the names of
  217.         all data fields used.
  218.  
  219.         """
  220.         id = self.id              #name of the column
  221.  
  222.         cidid = "%s/%s" % (cid,id)
  223.         has_key = request.has_key
  224.         if has_key(cidid): keys = request[cidid]
  225.         elif has_key(id): keys = request[id]
  226.         else: return None
  227.  
  228.         if type(keys) is not ListType: keys=[keys]
  229.         index = self._index
  230.         r = None
  231.         anyTrue = 0
  232.         opr = None
  233.  
  234.         if request.has_key(id+'_usage'):
  235.             # see if any usage params are sent to field
  236.             opr=string.split(string.lower(request[id+"_usage"]),':')
  237.             opr, opr_args=opr[0], opr[1:]
  238.  
  239.         if opr=="range":
  240.             if 'min' in opr_args: lo = min(keys)
  241.             else: lo = None
  242.             if 'max' in opr_args: hi = max(keys)
  243.             else: hi = None
  244.  
  245.             anyTrue=1
  246.             try:
  247.                 if hi:
  248.                     setlist = index.items(lo,hi)
  249.                 else:
  250.                     setlist = index.items(lo)
  251.  
  252.                 for k, set in setlist:
  253.                     if r is None:
  254.                         r = set
  255.                     else:
  256.                         r = r.union(set)
  257.  
  258.             except KeyError:
  259.                 pass
  260.  
  261.         else:           #not a range
  262.             get = index.get
  263.             for key in keys:
  264.                 if key:
  265.                     anyTrue = 1
  266.                 set=get(key)
  267.                 if set is not None:
  268.                     if r is None:
  269.                         r = set
  270.                     else:
  271.                         r = r.union(set)
  272.  
  273.         if r is None:
  274.             if anyTrue:
  275.                 r=intSet()
  276.             else:
  277.                 return None
  278.  
  279.         return r, (id,)
  280.  
  281.  
  282.     def hasUniqueValuesFor(self, name):
  283.         ' has unique values for column NAME '
  284.         if name == self.id:
  285.             return 1
  286.         else:
  287.             return 0
  288.  
  289.  
  290.     def uniqueValues(self, name=None, withLengths=0):
  291.         """\
  292.         returns the unique values for name
  293.  
  294.         if withLengths is true, returns a sequence of
  295.         tuples of (value, length)
  296.         """
  297.         if name is None:
  298.             name = self.id
  299.         elif name != self.id:
  300.             return []
  301.         if not withLengths: return tuple(
  302.             filter(nonEmpty,self._index.keys())
  303.             )
  304.         else: 
  305.             rl=[]
  306.             for i in self._index.keys():
  307.                 if not nonEmpty(i): continue
  308.                 else: rl.append((i, len(self._index[i])))
  309.             return tuple(rl)
  310.  
  311.  
  312.     def clear(self):
  313.         self._index = BTree.BTree()
  314.         self._unindex = IOBTree.BTree()
  315.  
  316.  
  317.  
  318.